package cn.signalpha.modules.pay.granter;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.signalpha.common.cache.ConfigCache;
import cn.signalpha.common.cache.ConfigKeys;
import cn.signalpha.common.dto.ConfigDTO;
import cn.signalpha.core.http.util.HttpUtil;
import cn.signalpha.core.log.exception.ServiceException;
import cn.signalpha.core.tool.jackson.JsonUtil;
import cn.signalpha.core.tool.utils.StringPool;
import cn.signalpha.core.tool.utils.StringUtil;
import cn.signalpha.modules.pay.dto.PayDTO;
import cn.signalpha.modules.pay.dto.RefundDTO;
import cn.signalpha.modules.pay.dto.SettleOrderDTO;
import cn.signalpha.modules.pay.dto.SettleUserDTO;
import cn.signalpha.modules.pay.enums.PayClientEnum;
import cn.signalpha.modules.pay.enums.PayTypeEnum;
import cn.signalpha.modules.pay.provider.IPayGranter;
import cn.signalpha.modules.pay.vo.RefundVO;
import cn.signalpha.modules.pay.vo.SettleOrderVO;
import cn.signalpha.modules.pay.vo.SettleUserVO;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 阿里支付
 *
 * @author signalpha
 */
@Slf4j
@Component
@AllArgsConstructor
public class JoinPayGranter implements IPayGranter {

	private final WxMaService wxMaService;

	@Override
	public String getPayVO(PayDTO payDTO) {
		payDTO.setNotifyUrl(payDTO.getNotifyUrl().concat("/join"));
		switch (Objects.requireNonNull(PayTypeEnum.instance(payDTO.getPayType()), "支付类型错误")){
			case JOIN_PAY_WX:
				switch (Objects.requireNonNull(PayClientEnum.instance(payDTO.getPayClient()), "支付终端错误")){
					case H5:
						return joinPay(payDTO, "WEIXIN_H5_PLUS");
					case APP:
						return joinPay(payDTO, "WEIXIN_APP3");
					case PUBLIC:
						return joinPay(payDTO, "WEIXIN_GZH");
					case MINI_APP:
						return joinPay(payDTO, "WEIXIN_XCX");
					default:
						throw new ServiceException("终端未实现");
				}
			case JOIN_PAY_ALI:
				switch (Objects.requireNonNull(PayClientEnum.instance(payDTO.getPayClient()), "支付终端错误")){
					case PC:
						return joinPay(payDTO, "ALIPAY_SYT");
					case H5:
						return joinPay(payDTO, "ALIPAY_H5");
					default:
						throw new ServiceException("终端未实现");
				}
			case JOIN_PAY_FAST:
//				return joinPay(payDTO);
			default:
				throw new ServiceException("支付类型错误");
		}
	}

	@Override
	public String verifyNotify(Map<String, String> params) {
		String returnHmac = params.remove("hmac");
		String paramsStr = createLinkStringByGet(params.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
		String hmac1 = signByMD5(paramsStr);
		return hmac1.equalsIgnoreCase(returnHmac) ? StringPool.TRUE : StringPool.FALSE;
	}

	@Override
	@SneakyThrows
	public RefundVO refund(RefundDTO refundDTO) {
		return RefundVO.builder().code(StringPool.NO).build();
	}

	@Override
	public SettleUserVO settleUser(SettleUserDTO settleUserDTO) {
		return SettleUserVO.builder().code(StringPool.NO).msg("该支付未实现分账").build();
	}

	@Override
	public SettleOrderVO settleOrder(SettleOrderDTO settleOrderDTO) {
		return SettleOrderVO.builder().code(StringPool.NO).msg("该支付未实现分账").build();
	}

	/**
	 * H5 支付
	 */
	private String joinPay(PayDTO payDTO, String frpCode) {
		String openId = "";
		if(StringUtil.isNoneBlank(payDTO.getCode(), payDTO.getAppId())) {
			try {
				openId = wxMaService.getUserService().getSessionInfo(payDTO.getCode()).getOpenid();
			} catch (WxErrorException e) {
				throw new ServiceException(e.getError().getErrorMsg());
			}
		}

		ConfigDTO.JoinPayDTO config = ConfigCache.getObject(ConfigKeys.JOIN_PAY, ConfigDTO.JoinPayDTO.class);
		Map<String, Object> params = new HashMap<>();
		params.put("p0_Version", "2.3");
		params.put("p1_MerchantNo", config.getMerchantNo());
		params.put("p2_OrderNo", payDTO.getPayNo());
		params.put("p3_Amount", payDTO.getPayMoney());
		params.put("p4_Cur", "1");
		params.put("p5_ProductName", payDTO.getBody());
		params.put("p7_Mp", payDTO.getNotifyParams());
		params.put("p9_NotifyUrl", payDTO.getNotifyUrl());
		params.put("q1_FrpCode", frpCode);
		params.put("q5_OpenId", openId);
		params.put("qa_TradeMerchantNo", config.getTradeMerchantNo());
		params.put("q7_AppId", config.getAppId());

		String mapStr = createLinkStringByGet(params);
		String sign = signByMD5(mapStr);
		params.put("hmac", sign);

		String resultStr = HttpUtil.post("https://www.joinpay.com/trade/uniPayApi.action", params);
		Map<String, Object> resultMap = JsonUtil.toMap(resultStr);
		String verify = verifyNotify(resultMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> String.valueOf(e.getValue()))));
		if(verify.equals(StringPool.FALSE)){
			throw new ServiceException("聚合支付验签失败");
		}
		if(!"100".equals(String.valueOf(resultMap.get("ra_Code")))){
			throw new ServiceException(StringUtil.format("拉起支付失败[{}]", resultMap.get("rb_CodeMsg")));
		}
		return String.valueOf(resultMap.get("rc_Result"));
	}

	/**
	 * 组装加密参数
	 */
	private String createLinkStringByGet(Map<String, Object> params){
		List<String> keys = new ArrayList<>(params.keySet());
		Collections.sort(keys);
		StringBuilder str1 = new StringBuilder();
		for(int i = 0; i < keys.size(); i++) {
			String key = keys.get(i);
			Object value = params.get(key);
			if(i == keys.size() - 1) {
				str1.append(value.toString());
			}else {
				str1.append(value);
			}
		}
		return str1.toString();
	}

	/**
	 * MD5签名
	 *  @param requestSign 请求签名串
	 *
	 */
	private String signByMD5(String requestSign) {
		ConfigDTO.JoinPayDTO config = ConfigCache.getObject(ConfigKeys.JOIN_PAY, ConfigDTO.JoinPayDTO.class);
		String reqHmac = "";
		try {
			reqHmac = DigestUtils.md5Hex(requestSign + config.getMd5()).toUpperCase();
		} catch (Exception ignored) {

		}
		return reqHmac;
	}

}
